package manager

import (
	"context"
	"github.com/tianjigames/fairy/constants"
	"github.com/tianjigames/fairy/dao"
	"github.com/tianjigames/fairy/model"
	"github.com/tianjigames/fairy/pojo"
	"github.com/tianjigames/fairy/protos"
	"github.com/tianjigames/fairy/util"
	"github.com/topfreegames/pitaya"
	"github.com/topfreegames/pitaya/logger"
	"strings"
	"sync"
	"time"
)

var (
	PlayerManager *playerManager
	playerOnce sync.Once
)

/**
玩家管理器
 */
type playerManager struct {
	BaseManager
	onlinePlayerIdLinkMap map[int64]int //sessionId与playerId映射关系
	onlinePlayerMap map[int]*model.PlayerObject //playerId与PlayerObject映射关系
}

func NewPlayerManager() *playerManager {
	playerOnce.Do(func() {
		PlayerManager = &playerManager{}
	})
	return PlayerManager
}

func (p *playerManager) CreateLocalAccount(username,channelId,password,macid,deviceModel,appId string) (*pojo.AccountData,error) {
	accountId,err := dao.GuidGeneratorDao.GenGuid(int(constants.GuidTypeAccount))
	if err != nil {
		logger.Log.Errorf("CreateLocalAccount failed,error:%s",err.Error())
		return nil,err
	}

	accountData := &pojo.AccountData{
		AccountId:      accountId,
		ChannelId:      channelId,
		Username:       username,
		AppId:          appId,
		Password:       password,
		Macid:          macid,
		AllPlayerGuids: "",
		CreateTime:     time.Now(),
		ActiveCode:     "",
		DeviceModel:    deviceModel,
	}

	err = dao.AccountDataDao.Add(accountData)
	if err != nil {
		logger.Log.Errorf("CreateLocalAccount failed,error:%s",err.Error())
		return nil,err
	}

	return accountData,nil
}

func (p *playerManager) ListPlayerData(accountData *pojo.AccountData,checkServerId bool) ([]*pojo.PlayerData,error) {
	if accountData == nil {
		return nil,nil
	}

	logger.Log.Infof("ListPlayerData accountId=%d,worldId=%d",accountData.AccountId,accountData.WorldId)
	if len(accountData.AllPlayerGuids) == 0 {
		return nil,nil
	}


	playerDataSlice := make([]*pojo.PlayerData,0)
	allGuids := strings.Split(accountData.AllPlayerGuids,",")
	hasBadGuid := false
	for _,str := range allGuids {
		if len(str) == 0{
			continue
		}

		guid := util.CastLong(str)
		if guid <= 0 {
			continue
		}

		v,err := dao.PlayerDataDao.Get(&pojo.PlayerData{Guid: guid})
		if err != nil {
			logger.Log.Errorf("ListPlayerData failed,guid=%d,error:%s",guid,err.Error())
			return nil,err
		}

		if v == nil {
			hasBadGuid = true
			logger.Log.Errorf("ListPlayerData accountId=%d,guid=%d hasBadGuid",accountData.AccountId,guid)
			accountData.CutPlayerGuid(guid)
			continue
		}

		playData := v.(*pojo.PlayerData)
		if playData.Status == pojo.RoleStateDelete || playData.GetStatus() == pojo.OptDelete {
			continue
		}

		if checkServerId && accountData.WorldId != 0 && accountData.WorldId != playData.WorldId {
			continue
		}

		playerDataSlice = append(playerDataSlice,playData)
	}

	if hasBadGuid {
		err := dao.AccountDataDao.Update(accountData,"accountId=?",accountData.AccountId)
		if err != nil {
			logger.Log.Errorf("ListPlayerData failed,accountId=%d,error:%s",accountData.AccountId,err.Error())
			return nil,err
		}
	}

	return playerDataSlice,nil
}

func (p *playerManager) CreatePlayer(accountData *pojo.AccountData,roleData *pojo.PlayerData) error {
	err := dao.PlayerDataDao.CreatePlayer(accountData,roleData)
	if err != nil {
		logger.Log.Errorf("CreatePlayer failed,err:%s",err.Error())
		return err
	}

	return nil
}


/**
删除某个角色
 */
func (p *playerManager) DeletePlayer(accountData *pojo.AccountData,playerData *pojo.PlayerData) error {
	err := dao.PlayerDataDao.DeletePlayer(accountData,playerData)
	if err != nil {
		logger.Log.Errorf("DeletePlayer failed,err:%s",err.Error())
		return err
	}

	//todo:删除world服务器上的role

	return nil
}

func (p *playerManager) GetAccountDataByUsernameAndChannelId(username,channelId string) ([]*pojo.AccountData,error) {
	r := make([]*pojo.AccountData,0)
	err := dao.AccountDataDao.GetListByDb(&pojo.AccountData{
		Username: username,
		ChannelId: channelId,
	},&r)
	if err != nil {
		logger.Log.Errorf("GetAccountDataByUsernameAndChannelId failed,error:%s",err.Error())
		return nil,err
	}
	return r,nil
}

func (p *playerManager) GetAccountDataByUsernameAndChannelIdAndAppId(username,channelId,appId string) (*pojo.AccountData,error) {
	r,err := dao.AccountDataDao.Get(&pojo.AccountData{
		Username: username,
		ChannelId: channelId,
		AppId: appId,
	})
	if err != nil {
		logger.Log.Errorf("GetAccountDataByUsernameAndChannelIdAndAppId failed,error：%s",err.Error())
		return nil,err
	}

	if r == nil {
		return nil,nil
	}

	return r.(*pojo.AccountData),nil
}


func (p *playerManager) RegisterByUserId()  {
	
}


func (p *playerManager) GetAccountDataByAccountId(accountId int64,appId string) (*pojo.AccountData,error) {
	r,err := dao.AccountDataDao.Get(&pojo.AccountData{
		AccountId: accountId,
		AppId: appId,
	})
	if err != nil {
		logger.Log.Errorf("[PlayerManager] GetAccountDataByAccountId failed,err:%s",err.Error())
		return nil,err
	}

	if r == nil {
		return nil,nil
	}

	return r.(*pojo.AccountData),nil
}

/**
通过playerId获取playerObject
 */
func (p *playerManager) GetOnLinePlayerByPlayerId(playerId int) *model.PlayerObject {
	if v,ok := p.onlinePlayerMap[playerId];ok {
		return v
	}

	return nil
}

func (p *playerManager) GetOnLinePlayerByGuidId(guid int64) *model.PlayerObject {
	if v,ok := p.onlinePlayerIdLinkMap[guid];ok {
		return p.GetOnLinePlayerByPlayerId(v)
	}

	return nil
}

/**
设置玩家的组队信息
 */
func (p *playerManager) SetPlayerObjectTeamInfo(player *model.PlayerObject,teamInfo *protos.MsgTeamInfo,bCreate bool) error {
	player.TeamInfo = p.ModifyPlayerObjectTeamInfo(teamInfo)
	TeamInfoManager.RefreshTeamMap(teamInfo)

	if bCreate {
		if player.PreInvitedPlayerGuid > 0 {//创建组队 邀请一个玩家加入
			TeamInfoManager.InviteJoinTeam(player,player.PreInvitedPlayerGuid,protos.InviteType_JoinSceneTeam)
			player.PreInvitedPlayerGuid = -1
		}
	}

	if len(player.FollowIds) > 0 {//检查组队跟随
		err := TeamInfoManager.CheckLeaderFollow(player)
		if err != nil {
			logger.Log.Errorf("[PlayerManager] SetPlayerObjectTeamInfo failed,error:%s",err.Error())
			return err
		}
	}

	err := TeamInfoManager.SendRefreshPlayerTeamInfo(player)
	if err != nil {
		logger.Log.Errorf("[PlayerManager] SetPlayerObjectTeamInfo failed,error:%s",err.Error())
		return err
	}

	return nil
}

/**
变更玩家组队信息
*/
func (p *playerManager) ModifyPlayerObjectTeamInfo(teamInfo *protos.MsgTeamInfo) *protos.MsgTeamInfo {
	if teamInfo == nil {
		return nil
	}

	builder := &protos.MsgTeamInfo{}
	util.CopyStruct(teamInfo,builder)

	if builder.TeamMemberList != nil && len(builder.TeamMemberList) > 0 {
		for _,v := range builder.TeamMemberList {
			playerTemp := p.GetOnLinePlayerByPlayerId(int(v.PlayerId))
			if playerTemp != nil {
				v.ChaoDuItemId = int32(playerTemp.GetChaoDuItemId())
			}
		}
	}
	return builder
}

/**
给某个玩家推送消息
 */
func (p *playerManager) SendPacket(player *model.PlayerObject,msgDatas ...*model.MsgData) error {
	if player == nil {
		return nil
	}

	if msgDatas != nil && len(msgDatas) > 0 {
		for _,v := range msgDatas {
			if v == nil || v.Msg == nil || len(v.Route) == 0{
				continue
			}

			mId := v.Stamp
			if mId <= 0 || player.GetScene() != nil {
				mId = uint(player.GetScene().GetSceneTickCount())
			}

			err := pitaya.SendPushToUserWithMid(v.Route,mId,v.Msg,player.SessionId,constants.SvTypeConnector)
			if err != nil {
				logger.Log.Errorf("[PlayerManager] SendPacket failed,error:%s",err.Error())
				return err
			}
		}
	}
	return nil
}

/**
给某个玩家的周围玩家发送消息
 */
func (p *playerManager) BroadCastMessage(scene *model.Scene,obj model.CharObj,retPacket *model.MsgData,bSendSelf bool) error {
	nZoneID := -1
	if obj != nil {
		nZoneID = obj.GetZoneID()
	}

	var pList []model.IBaseObj
	if scene.MSceneData != nil && scene.MSceneData.MnSceneTypeId == int(SceneTypeStCopy) {//玩家在副本中
		temp := scene.MObjManager.GetPlayerList()
		for _,v := range temp {
			pList = append(pList,v)
		}
	}else {
		//获取玩家周围的玩家
		pList = scene.GetAroundPlayerList(obj,scene.MZoneInfo.MNRefreshSize,nZoneID,protos.ObjType_ObtPlayer,bSendSelf)
	}

	if pList == nil || len(pList) == 0 {
		return nil
	}

	bCheckObj := false
	var player *model.PlayerObject
	ok := false
	if obj != nil {
		if player,ok = obj.(*model.PlayerObject);ok {
			bCheckObj = true
		}
	}

	for _,it := range pList {
		other,ok1 := it.(*model.PlayerObject)
		if !ok1 {
			continue
		}

		if bCheckObj && !bSendSelf && it.GetId() == player.GetId() {
			continue
		}

		if !player.CanViewMe(other) {
			continue
		}

		err := p.SendPacket(other,retPacket)
		if err != nil {
			logger.Log.Errorf("[PlayerManager] BroadCastMessage failed,error:%s",err.Error())
			return err
		}
	}

	return nil
}

/**
实例化游戏玩家对象
 */
func (p *playerManager) NewPlayerObject(data *pojo.PlayerData) (*model.PlayerObject,error) {
	obj := model.NewPlayerObject(data)
	weddingList,err := WeddingManager.LoadInvitationList(obj.GetGuid())
	if err != nil {
		logger.Log.Errorf("[PlayerManager] NewPlayerObject failed,error:%s",err.Error())
		return nil,err
	}
	obj.MWeddingData.InvitationDataList = weddingList
	return obj,nil
}

/**
更新排行榜
 */
func (p *playerManager) UpdateStandData(player *model.PlayerObject,nType,nValue int) error {
	if player == nil {
		return nil
	}

	msg := &protos.GWAddOrUpdateStandData{
		StandType: int32(nType),
		StandData: &protos.MsgStandData{
			Guid: player.GetGuid(),
			Name: player.GetRoleName(),
			Value: int32(nValue),
			Level: int32(player.GetLv()),
			CampId: int32(player.PlayerData.CampId),
			CareerId: int32(player.GetCareerId()),
			SexId: int32(player.SexId),
			GangId: player.GetGangId(),
			GangName: player.GangName,
			VipLv: int32(player.GetVipLevel()),
			CombatPower: int32(player.CombatPower),
		},
	}

	err := pitaya.RPC(context.Background(),constants.GWAddOrUpdateStandDataRoute,&protos.WGAddOrUpdateStandDataRet{},msg)
	if err != nil {
		logger.Log.Errorf("[PlayerManager] UpdateStandData failed,error:%s",err.Error())
		return err
	}
	return nil
}